package redisutil

import (
	"context"
	"errors"
	"github.com/redis/go-redis/v9"
	"github.com/rs/zerolog/log"
	"github.com/spf13/cast"
	"strconv"
)

var hashIncr = `
local step = ARGV[1]
local key = KEYS[1]
local currentField = KEYS[2]..'|cur|'..KEYS[3]
local maxField = KEYS[2]..'|max|'..KEYS[3]
local current = 0
local max = 0
local res = redis.call('hmget',key,currentField,maxField)
if (res[1])
then
	current = tonumber(res[1])
	max = tonumber(res[2])
	if (current + step <= max) then
		local currentNum = redis.call("HINCRBY",key,currentField,step)
		currentNum = tonumber(currentNum)
		if (currentNum > max) then 
			redis.call("HINCRBY",key,currentField,0-step)
			return max-currentNum
		else
			return max-currentNum
		end
	else
		return max-current
	end
	
    
else
    return -99999
end
`

func HashIncr(ctx context.Context,redisClient *redis.Client, key, field string, itemId, step int) int {
	script := redis.NewScript(hashIncr)
	result, err := script.Run(ctx, redisClient, []string{key, field, strconv.Itoa(itemId)}, step).Int()
	if err != nil {
		return 0
	}
	return result
}

var hashIncrGroup = `
local step = tonumber(KEYS[3])
local merge = tonumber(KEYS[4])
local key = KEYS[1]
local currentField = {}
for i,v in ipairs(ARGV) do
	table.insert(currentField,ARGV[i]..'|cur|'..KEYS[2])
	table.insert(currentField,ARGV[i]..'|max|'..KEYS[2])
end
local res = redis.call('hmget',key,unpack(currentField))
if (res[1])
then
	local index = 1
	if (merge == 1) then
		local curTotal =0
        local maxTotal = 0
        while(index <= #res/2) do
            curTotal = curTotal+ res[index*2-1]
            maxTotal = maxTotal+ res[index*2]
            index = index+1
        end
        if (curTotal + step <= maxTotal) then
            index = 1
            while(index <= #res/2) do
                local value  = res[index*2]-res[index*2-1]
                if (value < step) then
                    redis.call("HINCRBY",key,currentField[index*2-1],value)
                    step = step - value
                else
                    redis.call("HINCRBY",key,currentField[index*2-1],step)
                end
                index = index +1
            end
			return 100
        else
            return 10
        end
	else 
		while(index <= #res/2) do
            local value  = res[index*2]-res[index*2-1]
            if (value > 0) then
                if (step <= value) then
                    redis.call("HINCRBY",key,currentField[index*2-1],step)
                    return 100
                else
                    return 11
                end
            end
            index = index +1
        end
        return 12
	end
    
else
    return -1
end
`

func HashIncrGroup(ctx context.Context,redisClient *redis.Client,key string, itemId, step int, merge int, field ...string) int {
	script := redis.NewScript(hashIncrGroup)
	result, err := script.Run(ctx, redisClient, []string{key, strconv.Itoa(itemId), strconv.Itoa(step), strconv.Itoa(merge)}, field).Int()
	if err != nil {
		log.Error().Caller().Err(err).Msg("HashIncrGroup 执行异常")
		return -1
	}
	return result
}

func HashGroupLastTicket(ctx context.Context,redisClient *redis.Client, key string, itemId, step int, merge int, fields ...string) (int, error) {
	if len(fields) == 0 {
		return 0, errors.New("fields 参数不足")
	}

	res, err := redisClient.HGetAll(ctx, key).Result()
	if err != nil {
		return 0, err
	}
	var last = 0
	if merge == 1 {
		for _, field := range fields {
			curField := field + "|cur|" + strconv.Itoa(itemId)
			maxField := field + "|max|" + strconv.Itoa(itemId)
			last += cast.ToInt(res[maxField]) - cast.ToInt(res[curField])
		}
	} else {
		curField := fields[0] + "|cur|" + strconv.Itoa(itemId)
		maxField := fields[0] + "|max|" + strconv.Itoa(itemId)
		last = cast.ToInt(res[maxField]) - cast.ToInt(res[curField])
	}

	last = last - step
	if last < 0 {
		return last - step, errors.New("余票不足")
	}

	return last, nil
}
